﻿using NightskyMimic.Domain;
using NightskyMimic.Util;
using System.Collections.Generic;
using System.Linq;

namespace NightskyMimic.Core
{
    public class MinMax : Strategy
    {
        public List<Attack> GenerateAttackActions(Player max, Player min) 
        {
            List<Creature> tappedMin = min.Zone.Creatures.Where(x => x.Tapped).ToList();
            State root = new State(max.Zone.Life, new List<Creature>(max.Zone.Creatures), min.Zone.Life, new List<Creature>(min.Zone.Creatures), new List<Block>(), new List<Attack>(), null);

            var a = bestActions(root, true).Select(x => x as Attack).ToList();

            max.Zone.Creatures.ForEach(x => x.Tapped = false);
            min.Zone.Creatures.ForEach(x => x.Tapped = false);
            foreach (Creature c in tappedMin) 
            {
                min.Zone.Creatures.First(x => x.Id == c.Id).Tapped = true;
            }

            //string arvore = Grapviz.Graph(root);
            return a;
        }

        public List<Block> GenerateBlockActions(Player max, Player min, List<Attack> attack)
        {
            List<Creature> tappedMin = min.Zone.Creatures.Where(x => x.Tapped).ToList();
            List<Creature> tappedMax = max.Zone.Creatures.Where(x => x.Tapped).ToList();
            State root = new State(max.Zone.Life, new List<Creature>(max.Zone.Creatures), min.Zone.Life, new List<Creature>(min.Zone.Creatures), new List<Block>(), attack, null);
            

            var a = bestActions(root, false).Select(x => x as Block).ToList();
            max.Zone.Creatures.ForEach(x => x.Tapped = false);
            foreach (Creature c in tappedMax)
            {
                max.Zone.Creatures.First(x => x.Id == c.Id).Tapped = true;
            }

            min.Zone.Creatures.ForEach(x => x.Tapped = false);
            foreach (Creature c in tappedMin)
            {
                min.Zone.Creatures.First(x => x.Id == c.Id).Tapped = true;
            }

            //string arvore = Grapviz.Graph(root);

            return a;
        }

        //block, attack
        internal List<State> childStates(State state, bool isMaxTurn) 
        {
            List<State> blockStates = new List<State>();
            if (state.Attack == null || state.Attack.Count == 0)
                blockStates.Add(new State(state.Life, state.Creatures, state.OpponentLife, state.OpponentCreatures, new List<Block>(), new List<Attack>(), state));

            else
            {
                List<Creature> rightCreatures = state.RightCreatures(isMaxTurn);

                //all blocks for the attack
                List<List<Block>> allBlocks = Combinations.AllBlocks(rightCreatures, state.Attack);

                //generate states after attack
                for (int i = 0; i < allBlocks.Count; i++) 
                {
                    blockStates.Add(simulateCombat(state.Attack, allBlocks[i], state, isMaxTurn));
                }
            }

            List<State> result = new List<State>();
            //generate new attacks
            foreach (State s in blockStates)
            {
                var rightACreatures = s.RightCreatures(isMaxTurn);
                //when attack, the turns begins and all permanents are untaped
                rightACreatures.ForEach(x => x.Tapped = false);
                List<List<Attack>> attacks = Combinations.AllAttacks(rightACreatures);
                foreach (List<Attack> a in attacks)
                {
                    var newState = new State(s.Life, new List<Creature>(s.Creatures), s.OpponentLife, new List<Creature>(s.OpponentCreatures), s.Block, a, s.Parent);
                    result.Add(newState);
                    s.Parent.Children.Add(newState);
                }
            }

            state.Children.OrderBy(x => x.Utility);

            return result;
        }

        private State simulateCombat(List<Attack> attacks, List<Block> blocks, State oldState, bool isMaxTurn)
        {
            List<List<Creature>> deadCreatures = Battle.Combat(new List<Attack>(attacks), new List<Block>(blocks));
            List<Creature> maxCreatures = new List<Creature>(oldState.Creatures);
            List<Creature> minCreatures = new List<Creature>(oldState.OpponentCreatures);

            foreach (Creature c in deadCreatures[0])
                maxCreatures.Remove(c);

            foreach (Creature c in deadCreatures[1])
                minCreatures.Remove(c);

            List<Attack> notBLockedAttacks = attacks.Where(x => !x.Resolved).ToList();
            notBLockedAttacks.ForEach(x => x.Target.Tapped = true);
            int totalDamage = Battle.CombatDamage(notBLockedAttacks);
            int maxLife = isMaxTurn ? oldState.Life - totalDamage : oldState.Life;
            int minLife = isMaxTurn ? oldState.OpponentLife : oldState.OpponentLife - totalDamage;

            return new State(maxLife, maxCreatures, minLife, minCreatures, blocks, attacks, oldState);
        }

        internal State max(State s1, State s2) 
        {
            return s1.Utility > s2.Utility ? s1 : s2;
        }

        internal State min(State s1, State s2) 
        {
            return s1.Utility < s2.Utility ? s1 : s2;
        }

        internal State backtracking(State leaf) 
        {
            var result = leaf;
            while (result.Parent.Parent != null) 
            {
                result = result.Parent;
            }
            return result;
        }

        internal virtual State maxState(State state) 
        {
            if(state.Depth == Parameters.TurnsForward || state.isTerminal)
                return state;

            State v = new MinState();
            List<State> children = childStates(state, true);
            foreach (State child in children) 
                v = max(v, minState(child));

            return v;
        }

        internal virtual State minState(State state)
        {
            if (state.Depth == Parameters.TurnsForward || state.isTerminal)
                return state;

            State v = new MaxState();
            List<State> children = childStates(state, false);
            foreach (State child in children) 
                v = min(v, maxState(child));

            return v;
        }

        internal virtual List<Action> bestActions(State root, bool isAttack)
        {
           State result = backtracking(maxState(root));

           result.Attack.ForEach(x => x.Resolved = false);
           result.Block.ForEach(x => x.Resolved = false);

           return isAttack ? result.Attack.Select(x => x as Action).ToList() : result.Block.Select(x => x as Action).ToList();
        }
    }
}